﻿using Org.BouncyCastle.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using Splat;
using ZstdSharp.Unsafe;
using System.Linq.Expressions;

namespace Common.Utils
{
    public delegate void OutAction(out string y);

    public delegate void OutAction<T>(T t, out string y);

    public delegate T2 OutFunc<T1, out T2>(T1 t, out string y);

    public delegate void OutAction<T1, T2>(T1 t, T2 t2, out string y);
    public delegate void OutAction<T1, T2, T3>(T1 t, T2 t2, T3 t3, out string y);

    public delegate T3 OutFunc<T1, T2, out T3>(T1 t, T2 t2, out string y);

    public interface IError
    {
        public string Error { get; set; }
    }

    public static class RetryFunc
    {
        public static T Try<T>(this Func<T> func, int times = 3)
        {
            Exception e = null;
            while (times > 0)
            {
                try
                {
                    var mr = func.Method;
                    var pa = func.Method.GetParameters();

                    return func();
                }
                catch (Exception ex)
                {
                    e = ex;
                    times--;
                }
            }
            throw e;
        }

        public static F Try<T, F>(Expression<OutFunc<T, F>> func, int times = 3) where F : class
        {
            //error = "";
            while (times > 0)
            {
                try
                {

                    //if (string.IsNullOrEmpty(error))
                    //{
                    //    break;
                    //}

                    times--;
                }
                catch (Exception ex)
                {
                    times--;
                }
            }

            return null;
        }

        public static void Try(this OutAction func, int times = 3)
        {
            while (times > 0)
            {
                try
                {
                    func(out string error);
                    if (string.IsNullOrEmpty(error))
                    {
                        break;
                    }

                    times--;
                }
                catch (Exception ex)
                {
                    times--;
                }
            }
        }


        public static T3 Try<T1, T2, T3>(this OutFunc<T1, T2, T3> func, T1 t1, T2 t2, out string error, int times = 3)
        {
            var t3 = func(t1, t2, out error);
            while (times > 0)
            {
                try
                {
                    if (string.IsNullOrEmpty(error))
                    {
                        return t3;
                        break;
                    }

                    t3 = func(t1, t2, out error);


                    times--;
                }
                catch (Exception ex)
                {
                    times--;
                }
            }

            return t3;
        }

        public static void Try<T1, T2>(this OutAction<T1, T2> func, T1 t1, T2 t2, out string error, int times = 3)
        {
            func(t1, t2, out error);
            while (times > 0)
            {
                try
                {
                    if (string.IsNullOrEmpty(error))
                    {
                        return;
                        break;
                    }
                    func(t1, t2, out error);
                    times--;
                }
                catch (Exception ex)
                {
                    times--;
                }
            }

            return;
        }

        public static void Try<T1, T2, T3>(this OutAction<T1, T2, T3> func, T1 t1, T2 t2, T3 t3, out string error, int times = 3)
        {
            func(t1, t2, t3, out error);
            while (times > 0)
            {
                try
                {
                    if (string.IsNullOrEmpty(error))
                    {
                        return;
                        break;
                    }
                    func(t1, t2, t3, out error);
                    times--;
                }
                catch (Exception ex)
                {
                    times--;
                }
            }

            return;
        }

        //public static IError Try(this Func<IError> func, int times = 3)
        //{


        //    for (int i = 0; i < times-1; i++)
        //    {
        //        IError error = func.Invoke();
        //        if (string.IsNullOrEmpty(error.Error))
        //        {
        //            return error;
        //        }
        //    }

        //}

        //public static T Try<T1, T2, T>(this Func< T1,T2,T> func, int times = 3)
        //{
        //    Exception e = null;
        //    while (times > 0)
        //    {
        //        try
        //        {
        //            return func();
        //        }
        //        catch (Exception ex)
        //        {
        //            e = ex;
        //            times--;
        //        }
        //    }
        //    throw e;
        //}

        public static async Task<T> TryAsync<T>(Func<Task<T>> task, int times = 3)
        {
            Exception e = null;
            while (times > 0)
            {
                try
                {
                    return await task();
                }
                catch (Exception ex)
                {
                    e = ex;
                    times--;
                    await Task.Delay(1);
                }
            }
            throw e;
        }
    }
}
